package com.sifou.courses.service.impl;

import com.sifou.courses.dto.SeckillGoodsDto;
import com.sifou.courses.dto.SeckillOrderDto;
import com.sifou.courses.exception.CustomException;
import com.sifou.courses.repository.entity.TSeckillGoods;
import com.sifou.courses.repository.mapper.TSeckillGoodsMapper;
import com.sifou.courses.service.ISeckillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.retry.RetryException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 数据库乐观锁实现
 * 总结：乐观锁不是真的锁.而是使用一种机制来保证读后写的正确性.这种方式可能会大量重试.需要根据业务场景合理使用.
 * 缺陷：1. ABA 2. 资源浪费
 * @author liuzhongxu
 * @date 2020/8/18
 */
@SuppressWarnings("AlibabaRemoveCommentedCode")
@Slf4j
@Service("seckillOptimisticLockService")
public class SeckillOptimisticLockServiceImpl implements ISeckillService {

    @Resource
    private TSeckillGoodsMapper goodsMapper;

    @Override
    public SeckillGoodsDto checkStock(Integer id) {
        TSeckillGoods tSeckillGoods = goodsMapper.selectById(id);
        if (tSeckillGoods.getCount() > 0) {
            log.debug("库存充足：count=" + tSeckillGoods.getCount());
            SeckillGoodsDto goodsDto = new SeckillGoodsDto();
            BeanUtils.copyProperties(tSeckillGoods, goodsDto);
            return goodsDto;
        }
        throw new CustomException("库存不足：count=" + tSeckillGoods.getCount());
    }

    @Override
    public Integer saleStock(SeckillGoodsDto goodsDto) {
        TSeckillGoods tSeckillGoods = new TSeckillGoods();
        BeanUtils.copyProperties(goodsDto, tSeckillGoods);
        log.debug("库存：count={} 已售：sale={}", goodsDto.getCount(), goodsDto.getSale());
        return goodsMapper.updateByOptimisticLock(tSeckillGoods);
    }

    @Override
    public Integer rollBackStock(SeckillOrderDto orderDto) {
        TSeckillGoods tSeckillGoods = goodsMapper.selectById(orderDto.getGoodsId());
        Integer opCount =  goodsMapper.rollBackByOptimisticLock(tSeckillGoods);
        if (opCount > 0) {
            log.debug("回滚库存成功：count={} 已售：sale={} 订单：{}", tSeckillGoods.getCount(), tSeckillGoods.getSale(), orderDto);
            return opCount;
        }
        log.debug("回滚库存失败：count={} 已售：sale={} 订单：{}", tSeckillGoods.getCount(), tSeckillGoods.getSale(), orderDto);
        throw new RetryException("回滚库存失败");
    }
}
